Handle recursion from motion event handlers
authorOwen W. Taylor <otaylor@fishsoup.net>
Mon, 11 Nov 2013 23:04:34 +0000 (18:04 -0500)
committerMatthias Clasen <mclasen@redhat.com>
Tue, 12 Nov 2013 04:17:14 +0000 (23:17 -0500)
If a motion event handler (or other handler running from the flush-events
phase of the frame clock) recursed the main loop then flushing wouldn't
complete until after the recursed main loop returned, and various aspects
of the state would get out of sync.

To fix this, change flushing of the event queue to simply mark events as
ready to flush, and let normal event delivery handle the rest.

https://bugzilla.gnome.org/show_bug.cgi?id=705176

gdk/broadway/gdkeventsource.c
gdk/gdkdisplay.c
gdk/gdkdisplayprivate.h
gdk/gdkevents.c
gdk/gdkinternals.h
gdk/gdkwindow.c
gdk/quartz/gdkeventloop-quartz.c
gdk/wayland/gdkeventsource.c
gdk/win32/gdkevents-win32.c
gdk/x11/gdkeventsource.c

index e6c0a7f5a69dc53ce8832888214aaac1100ff13f..d7cdede0a4bc539f6eab2117100fb026e94cfe42 100644 (file)
@@ -62,10 +62,7 @@ gdk_event_source_prepare (GSource *source,
 
   *timeout = -1;
 
-  if (display->event_pause_count > 0)
-    retval = FALSE;
-  else
-    retval = (_gdk_event_queue_find_first (display) != NULL);
+  retval = (_gdk_event_queue_find_first (display) != NULL);
 
   gdk_threads_leave ();
 
@@ -80,9 +77,8 @@ gdk_event_source_check (GSource *source)
 
   gdk_threads_enter ();
 
-  if (event_source->display->event_pause_count > 0)
-    retval = FALSE;
-  else if (event_source->event_poll_fd.revents & G_IO_IN)
+  if (event_source->display->event_pause_count > 0 ||
+      event_source->event_poll_fd.revents & G_IO_IN)
     retval = (_gdk_event_queue_find_first (event_source->display) != NULL);
   else
     retval = FALSE;
index 10928698fd0c5373d900e12738f1c5d83f8ff5e3..f19319d5386d701c026e644a30f1f0c9aa5a543a 100644 (file)
@@ -321,10 +321,8 @@ gdk_display_get_event (GdkDisplay *display)
 {
   g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
 
-  if (display->event_pause_count > 0)
-    return NULL;
-
-  GDK_DISPLAY_GET_CLASS (display)->queue_events (display);
+  if (display->event_pause_count == 0)
+    GDK_DISPLAY_GET_CLASS (display)->queue_events (display);
 
   return _gdk_event_unqueue (display);
 }
@@ -2032,24 +2030,6 @@ _gdk_display_unpause_events (GdkDisplay *display)
   display->event_pause_count--;
 }
 
-void
-_gdk_display_flush_events (GdkDisplay *display)
-{
-  display->flushing_events = TRUE;
-
-  while (TRUE)
-    {
-      GdkEvent *event = _gdk_event_unqueue (display);
-      if (event == NULL)
-        break;
-
-      _gdk_event_emit (event);
-      gdk_event_free (event);
-    }
-
-  display->flushing_events = FALSE;
-}
-
 void
 _gdk_display_event_data_copy (GdkDisplay     *display,
                               const GdkEvent *event,
index 365b429c33a03799777e1a7e70533c38b09f3775..df79f59993b8a61cfdbdb55094bcb1a0d3c9a036 100644 (file)
@@ -116,7 +116,6 @@ struct _GdkDisplay
   guint event_pause_count;       /* How many times events are blocked */
 
   guint closed             : 1;  /* Whether this display has been closed */
-  guint flushing_events    : 1;  /* Inside gdk_display_flush_events */
 
   GArray *touch_implicit_grabs;
   GHashTable *device_grabs;
@@ -300,7 +299,6 @@ void                _gdk_display_pointer_info_foreach (GdkDisplay       *display
 gulong              _gdk_display_get_next_serial      (GdkDisplay       *display);
 void                _gdk_display_pause_events         (GdkDisplay       *display);
 void                _gdk_display_unpause_events       (GdkDisplay       *display);
-void                _gdk_display_flush_events         (GdkDisplay       *display);
 void                _gdk_display_event_data_copy      (GdkDisplay       *display,
                                                        const GdkEvent   *event,
                                                        GdkEvent         *new_event);
index 83c313cd3442196bc5ddefd83257ac8643b18c73..86ea3570fb3cb0c8ecfbf311d3071187be15ff3f 100644 (file)
@@ -88,20 +88,20 @@ _gdk_event_queue_find_first (GdkDisplay *display)
   GList *tmp_list;
   GList *pending_motion = NULL;
 
-  if (display->event_pause_count > 0)
-    return NULL;
+  gboolean paused = display->event_pause_count > 0;
 
   tmp_list = display->queued_events;
   while (tmp_list)
     {
       GdkEventPrivate *event = tmp_list->data;
 
-      if (!(event->flags & GDK_EVENT_PENDING))
+      if ((event->flags & GDK_EVENT_PENDING) == 0 &&
+         (!paused || (event->flags & GDK_EVENT_FLUSHED) != 0))
         {
           if (pending_motion)
             return pending_motion;
 
-          if (event->event.type == GDK_MOTION_NOTIFY && !display->flushing_events)
+          if (event->event.type == GDK_MOTION_NOTIFY && (event->flags & GDK_EVENT_FLUSHED) == 0)
             pending_motion = tmp_list;
           else
             return tmp_list;
@@ -321,6 +321,18 @@ _gdk_event_queue_handle_motion_compression (GdkDisplay *display)
     }
 }
 
+void
+_gdk_event_queue_flush (GdkDisplay *display)
+{
+  GList *tmp_list;
+
+  for (tmp_list = display->queued_events; tmp_list; tmp_list = tmp_list->next)
+    {
+      GdkEventPrivate *event = tmp_list->data;
+      event->flags |= GDK_EVENT_FLUSHED;
+    }
+}
+
 /**
  * gdk_event_handler_set:
  * @func: the function to call to handle events from GDK.
index b2456662ecd39a75e8c03f6e7b29a09eb26aca59..bebdec03b7e631349a227c4a6228f6a275532417 100644 (file)
@@ -156,7 +156,13 @@ typedef enum
    * 1) touch events emulating pointer events
    * 2) pointer events being emulated by a touch sequence.
    */
-  GDK_EVENT_POINTER_EMULATED = 1 << 1
+  GDK_EVENT_POINTER_EMULATED = 1 << 1,
+
+  /* When we are ready to draw a frame, we pause event delivery,
+   * mark all events in the queue with this flag, and deliver
+   * only those events until we finish the frame.
+   */
+  GDK_EVENT_FLUSHED = 1 << 2
 } GdkEventFlags;
 
 struct _GdkEventPrivate
@@ -305,6 +311,7 @@ GList* _gdk_event_queue_insert_before(GdkDisplay *display,
                                       GdkEvent   *event);
 
 void    _gdk_event_queue_handle_motion_compression (GdkDisplay *display);
+void    _gdk_event_queue_flush                     (GdkDisplay       *display);
 
 void   _gdk_event_button_generate    (GdkDisplay *display,
                                       GdkEvent   *event);
index 8c121ed7411eda7928e32e7ee65c12059646e7d7..f6330e2d7e3b9ff2bd0784f965476824c520fa72 100644 (file)
@@ -10696,7 +10696,7 @@ gdk_window_flush_events (GdkFrameClock *clock,
   window = GDK_WINDOW (data);
 
   display = gdk_window_get_display (window);
-  _gdk_display_flush_events (display);
+  _gdk_event_queue_flush (display);
   _gdk_display_pause_events (display);
 
   gdk_frame_clock_request_phase (clock, GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS);
index 2c1ca0b46747a73364fca718c9465b0b34731625..bc1ecbb3d8d843a3678762c7bc1378c13c033dfc 100644 (file)
@@ -647,7 +647,7 @@ gdk_event_prepare (GSource *source,
   *timeout = -1;
 
   if (_gdk_display->event_pause_count > 0)
-    retval = FALSE;
+    retval = _gdk_event_queue_find_first (_gdk_display) != NULL;
   else
     retval = (_gdk_event_queue_find_first (_gdk_display) != NULL ||
               _gdk_quartz_event_loop_check_pending ());
@@ -665,7 +665,7 @@ gdk_event_check (GSource *source)
   gdk_threads_enter ();
 
   if (_gdk_display->event_pause_count > 0)
-    retval = FALSE;
+    retval = _gdk_event_queue_find_first (_gdk_display) != NULL;
   else
     retval = (_gdk_event_queue_find_first (_gdk_display) != NULL ||
               _gdk_quartz_event_loop_check_pending ());
index 9dda53a1ffbb9656788e59ec09f62a68c871a7c4..11e7b1ef5916b982a609be60dd23537960fef5a0 100644 (file)
@@ -40,7 +40,7 @@ gdk_event_source_prepare(GSource *base, gint *timeout)
   *timeout = -1;
 
   if (source->display->event_pause_count > 0)
-    return FALSE;
+    return _gdk_event_queue_find_first (source->display) != NULL;
 
   /* We have to add/remove the GPollFD if we want to update our
    * poll event mask dynamically.  Instead, let's just flush all
@@ -64,7 +64,7 @@ gdk_event_source_check(GSource *base)
   GdkWaylandEventSource *source = (GdkWaylandEventSource *) base;
 
   if (source->display->event_pause_count > 0)
-    return FALSE;
+    return _gdk_event_queue_find_first (source->display) != NULL;
 
   return _gdk_event_queue_find_first (source->display) != NULL ||
     source->pfd.revents;
index e7524d0bc8416c18ad91feda336f38ae26b902b4..2f751ee0b90ddbbedfe1ece4fd33afd3b7c33cf8 100644 (file)
@@ -3328,7 +3328,7 @@ gdk_event_prepare (GSource *source,
   *timeout = -1;
 
   if (_gdk_display->event_pause_count > 0)
-    retval = FALSE;
+    retval =_gdk_event_queue_find_first (_gdk_display) != NULL;
   else
     retval = (_gdk_event_queue_find_first (_gdk_display) != NULL ||
               (modal_win32_dialog == NULL &&
@@ -3347,7 +3347,7 @@ gdk_event_check (GSource *source)
   gdk_threads_enter ();
 
   if (_gdk_display->event_pause_count > 0)
-    retval = FALSE;
+    retval = gdk_event_queue_find_first (_gdk_display) != NULL;
   else if (event_poll_fd.revents & G_IO_IN)
     retval = (_gdk_event_queue_find_first (_gdk_display) != NULL ||
               (modal_win32_dialog == NULL &&
index 4b1a5469459e78868e9a2a39ef5fcf3098309a07..7fff28f0c2f35189cfcf1ddbb099e2f76b33dce8 100644 (file)
@@ -278,7 +278,7 @@ gdk_event_source_prepare (GSource *source,
   *timeout = -1;
 
   if (display->event_pause_count > 0)
-    retval = FALSE;
+    retval = _gdk_event_queue_find_first (display) != NULL;
   else
     retval = (_gdk_event_queue_find_first (display) != NULL ||
               gdk_check_xpending (display));
@@ -297,7 +297,7 @@ gdk_event_source_check (GSource *source)
   gdk_threads_enter ();
 
   if (event_source->display->event_pause_count > 0)
-    retval = FALSE;
+    retval = _gdk_event_queue_find_first (event_source->display) != NULL;
   else if (event_source->event_poll_fd.revents & G_IO_IN)
     retval = (_gdk_event_queue_find_first (event_source->display) != NULL ||
               gdk_check_xpending (event_source->display));